package org.infodavid.common.registry.impl;

import java.io.Serializable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.infodavid.common.registry.IRegistry;
import org.infodavid.common.registry.IRegistryEvent;
import org.infodavid.common.registry.IRegistryListener;

/**
 * The Class AbstractRegistry.
 * @param <K> the type of the keys
 * @param <E> the type of the elements
 */
public abstract class AbstractRegistry<K extends Serializable,E extends Serializable> implements
    IRegistry<K,E> {

	/** Logger. */
	private static final Log LOGGER = LogFactory.getLog(AbstractRegistry.class);

	/**
	 * The Interface KeyGenerator.
	 * @param <ID> The type of the key
	 * @param <V> the type of the value
	 */
	public interface IKeyGenerator<ID extends Serializable,V extends Serializable> {

		/**
		 * Gets the key according to the given element.
		 * @param elt the element
		 * @return the key
		 */
		ID[] getKeys(V elt);
	}

	/** The key generator. */
	private final transient IKeyGenerator<K,E> keyGenerator;

	/** The listener. */
	private transient IRegistryListener<K,E> listener = null;

	/**
	 * Instantiates a new default registry.
	 * @param generator the key generator
	 */
	protected AbstractRegistry(final IKeyGenerator<K,E> generator) {
		super();

		keyGenerator = generator;
	}

	/**
	 * Instantiates a new default registry.
	 * @param generator the key generator
	 */
	protected AbstractRegistry(final IKeyGenerator<K,E> generator, final IRegistryListener<K,E> lsnr) {
		this(generator);

		listener = lsnr;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.registry.IRegistry#clear()
	 */
	public final void clear() {
		final long start = System.currentTimeMillis();

		LOGGER.debug("Clearing registry...");

		if (listener == null) {
			doClear();
		}
		else {
			final IRegistryEvent<K,E> evt = new RegistryEventImpl<K,E>(this, null, null);

			listener.preCleared(evt);

			doClear();

			listener.postCleared(evt);
		}

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Cleared in (ms): " + (System.currentTimeMillis() - start));
		}
	}

	/**
	 * Gets the key generator.
	 * @return the keyGenerator
	 */
	public IKeyGenerator<K,E> getKeyGenerator() {
		return keyGenerator;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.registry.IRegistry#register(java.io.Serializable)
	 */
	public final E register(final E elt) {
		final long start = System.currentTimeMillis();
		E result = null;

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Registering element: " + elt);
		}

		if (listener == null) {
			result = doRegister(elt);
		}
		else {
			final IRegistryEvent<K,E> evt = new RegistryEventImpl<K,E>(this, null, elt);

			listener.preRegister(evt);

			result = doRegister(elt);

			listener.postRegister(evt);
		}

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Registered in (ms): " + (System.currentTimeMillis() - start));
		}

		return result;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.registry.IRegistry#unregister(java.io.Serializable)
	 */
	public final E unregister(final E elt) {
		final long start = System.currentTimeMillis();
		E result = null;

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Unregistering element: " + elt);
		}

		if (listener == null) {
			result = doUnregister(elt);
		}
		else {
			final IRegistryEvent<K,E> evt = new RegistryEventImpl<K,E>(this, null, elt);

			listener.preUnregister(evt);

			result = doUnregister(elt);

			listener.postUnregister(evt);
		}

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Unregistered in (ms): " + (System.currentTimeMillis() - start));
		}

		return result;
	}

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.registry.IRegistry#getEntry(java.io.Serializable)
	 */
	public final E getEntry(final K key) {
		final long start = System.currentTimeMillis();
		E result = null;

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Retrieve element: " + key);
		}

		result = doGetEntry(key);

		if (listener != null && result == null) {
			final IRegistryEvent<K,E> evt = new RegistryEventImpl<K,E>(this, key, null);

			result = listener.undefined(evt);
		}

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Entry found: " + result);
			LOGGER.debug("Retrieved in (ms): " + (System.currentTimeMillis() - start));
		}

		return result;
	}

	/**
	 * Do clear.
	 */
	protected abstract void doClear();

	/**
	 * Do register.
	 * @param elt the element
	 * @return the element
	 */
	protected abstract E doRegister(E elt);

	/**
	 * Do get.
	 * @param key the key
	 * @return the element or null
	 */
	protected abstract E doGetEntry(final K key);

	/**
	 * Do unregister.
	 * @param elt the element
	 * @return the element
	 */
	protected abstract E doUnregister(E elt);

	/*
	 * (non-Javadoc)
	 * @see org.infodavid.common.registry.IRegistry#getListener()
	 */
	public IRegistryListener<K,E> getListener() {
		return listener;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * org.infodavid.common.registry.IRegistry#setListener(org.infodavid.common.registry.IRegistryListener
	 * )
	 */
	public void setListener(final IRegistryListener<K,E> lsnr) {
		listener = lsnr;
	}
}
